/**
 *
 *      Copyright 2008 Oliver Eichner <o.eichner@gmail.com>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 *
 * @file Device.cpp
 * Device Class to store informations from tableau-parm
 */

#include <stdlib.h>
#include "Device.h"

/** 
 * @brief Standart konstruktor for a Device Object
 * @param devName kernelname of device
 */
Device::Device(string devName) {
    deviceName = devName;
    bridge_dco_in_use = false;
    bridge_dco_support = false;
    bridge_declare_write_blocked = false;
    bridge_declare_write_errors = false;
    bridge_hpa_in_use = false;
    bridge_hpa_support = false;
    bridge_security_in_use = false;
    bridge_security_support = false;
    bridge_writes_permitted = false;
    fetchSys();
}

Device::~Device() {

}

void Device::setComment(string nComment) {
    comment = nComment;
}

string Device::getComment() {
    return comment;
}

string Device::getDeviceName() {
    return deviceName;
}

string Device::getVendor() {
    return vendor;
}

string Device::getModel() {
    return model;
}

string Device::getSerial() {
    return bridge_drive_serial;
}

int Device::getSizeMB() {
    return sizeMB;
}

string Device::getSizeBLK() {
    stringstream blksize;
    string out;
    blksize << (sizeBlk);
    blksize >> out;
    return out;
}

//Tableau-Bridge getter

string Device::getBridge_chan_index() {
    return bridge_chan_index;
}

string Device::getBridge_chan_type() {
    return bridge_chan_type;
}

bool Device::getBridge_writes_permitted() {
    return bridge_writes_permitted;
}

bool Device::getBridge_declare_write_blocked() {
    return bridge_declare_write_blocked;
}

bool Device::getBridge_declare_write_errors() {
    return bridge_declare_write_errors;
}

string Device::getBridge_serial() {
    return bridge_serial;
}

string Device::getBridge_vendor() {
    return bridge_vendor;
}

string Device::getBridge_model() {
    return bridge_model;
}

string Device::getBridge_firmware_date() {
    return bridge_firmware_date;

}

string Device::getBridge_firmware_time() {
    return bridge_firmware_time;
}
// ## Drive Information ##

string Device::getBridge_drive_vendor() {
    return bridge_drive_vendor;
}

string Device::getBridge_drive_model() {
    return bridge_drive_model;
}

string Device::getBridge_drive_serial() {
    return bridge_drive_serial;
}

string Device::getBridge_drive_revision() {
    return bridge_drive_revision;
}

//## Drive HPA/DCO/Security Information ##

bool Device::getBridge_security_in_use() {
    return bridge_security_in_use;
}

bool Device::getBridge_security_support() {
    return bridge_security_support;
}

bool Device::getBridge_dco_support() {
    return bridge_dco_support;
}

bool Device::getBridge_dco_in_use() {
    return bridge_dco_in_use;
}

bool Device::getBridge_hpa_support() {
    return bridge_hpa_support;
}

bool Device::getBridge_hpa_in_use() {
    return bridge_hpa_in_use;
}

string Device::getBridge_drive_capacity() {
    return bridge_drive_capacity;
}

string Device::getBridge_hpa_capacity() {
    return bridge_hpa_capacity;
}

string Device::getBridge_dco_capacity() {
    return bridge_dco_capacity;
}

string Device::getBridge_dco_challenge_key() {
    return bridge_dco_challenge_key;
}

/**
 * @brief Get the size-string of the device either in MB,GB or TB, dependend on sizeMB..
 */
string Device::getSize() {
    if (sizeMB > 0) {
        stringstream sstr;

        if (sizeMB < 999) {
            sstr << sizeMB;
            sstr >> size;
            size.append(" MB");

        } else if (sizeMB > 999 && sizeMB <= 999999) {
            sstr << (sizeMB / 1000);
            sstr >> size;
            size.append(" GB");
        } else if (sizeMB > 999999) {
            sstr << (sizeMB / 1000000);
            sstr >> size;
            size.append(" TB");
        }
        cout << "newSize: " << size << endl;
        return size;
    } else
        return "error";
}

/**
 * @brief Fetch /sys/block informations
 */
void Device::fetchSys() {
    fstream Datei;
    string text, sysFile, tmpSysFile;
    sysFile = "/sys/block/";
    sysFile += deviceName;


    //size abfragen
    tmpSysFile = sysFile + "/size";
    Datei.open(tmpSysFile.data(), ios::in);
    getline(Datei, text);
    cout << "sizeLine:" << text << endl;
    
    sizeBlk = atol(text.data());
    cout << "sizeBlk:" << sizeBlk << endl;
    cout << "stest: "<< (((sizeBlk * 512) / 1000) / 1000) << endl;
    sizeMB = (int) (((sizeBlk * 512) / 1000) / 1000);
    cout << "sizeMB:" << sizeMB << endl;

    Datei.close();



    sysFile += "/device/";

    //Model name abfragen
    tmpSysFile = sysFile;
    tmpSysFile += "model";
    Datei.open(tmpSysFile.data(), ios::in);
    getline(Datei, text);
    model = text;
    //remoce spaces at the end of the string
    while (model.find_last_of(" ") == model.size() - 1) {

        model.erase(model.find_last_of(" "));
    }
    if (!model.empty()) {
        cout << "model gefunden: " << model << endl;

    } else {
        cerr << "kein model name gefunden" << endl;
        model = "n/a";
    }

    Datei.close();

    //Vendor name abfragen
    tmpSysFile.clear();
    tmpSysFile = sysFile;
    tmpSysFile += "vendor";
    Datei.open(tmpSysFile.data(), ios::in);

    getline(Datei, text);
    vendor = text;
    //remoce spaces at the end of the string
    while (vendor.find_last_of(" ") == vendor.size() - 1) {

        vendor.erase(vendor.find_last_of(" "));
    }
    if (!vendor.empty()) {
        cout << "vendor gefunden: " << vendor << endl;
    } else {
        cerr << "kein vendor name gefunden" << endl;
        vendor = "n/a";
    }
    Datei.close();


    fetchTableauParmStats();
}

/**
 * @brief scans drives with tableau-parm
 */
void Device::fetchTableauParmStats() {
    FILE *tableau_parm;
    string command, temp_buf;
    int len, found_pos;
    char buffer[127];


    //TODO nicer authentication...
    //if (getenv("USER") != "root") {
    //    command = "gksu  --message 'gTableauParm need root privileges to execute \"tableau-parm\" propably. Please enter root password below:'  tableau-parm /dev/" + getDeviceName();
    //} else {
        command = "tableau-parm /dev/" + getDeviceName();
    //}
    if ((tableau_parm = popen(command.data(), "r")) == NULL) {
        perror("popen()");

    }
    while (fgets(buffer, sizeof ( buffer), tableau_parm) != NULL) {
        // parse tableau-parm output
        temp_buf = buffer;
        len = temp_buf.length();
        temp_buf.erase(len - 1, len);
        cout << "tableau-parm: " << temp_buf << endl;
        if (temp_buf.find("chan_index:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_chan_index = temp_buf;

        } else if (temp_buf.find("chan_type:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_chan_type = temp_buf;
        } else if (temp_buf.find("writes_permitted:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_writes_permitted = (temp_buf == "TRUE" ? true : false);
            //cout << "writes[" << temp_buf << "]:" << (bridge_writes_permitted ? "yes" : "no") << endl;
        } else if (temp_buf.find("declare_write_blocked:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_declare_write_blocked = (temp_buf == "TRUE" ? true : false);
            //cout << "writes[" << temp_buf << "]:" << (bridge_declare_write_blocked ? "yes" : "no") << endl;
        } else if (temp_buf.find("declare_write_errors:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_declare_write_errors = (temp_buf == "TRUE" ? true : false);
        } else if (temp_buf.find("bridge_serial:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_serial = temp_buf;
        } else if (temp_buf.find("bridge_vendor:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_vendor = temp_buf;
        } else if (temp_buf.find("bridge_model:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_model = temp_buf;
        } else if (temp_buf.find("firmware_date:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_firmware_date = temp_buf;
        } else if (temp_buf.find("firmware_time:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_firmware_time = temp_buf;
        } else if (temp_buf.find("drive_vendor:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_drive_vendor = temp_buf;
        } else if (temp_buf.find("drive_model:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_drive_model = temp_buf;
        } else if (temp_buf.find("drive_serial:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_drive_serial = temp_buf;
        } else if (temp_buf.find("drive_revision:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_drive_revision = temp_buf;
        } else if (temp_buf.find("security_in_use:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_security_in_use = (temp_buf == "TRUE" ? true : false);
        } else if (temp_buf.find("security_support:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_security_support = (temp_buf == "TRUE" ? true : false);
        } else if (temp_buf.find("hpa_in_use:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_hpa_in_use = (temp_buf == "TRUE" ? true : false);
        } else if (temp_buf.find("hpa_support:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_hpa_support = (temp_buf == "TRUE" ? true : false);
            //cout << "hpa support[" << temp_buf << "]:" << (bridge_hpa_support ? "yes" : "no") << endl;
        } else if (temp_buf.find("dco_in_use:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_dco_in_use = (temp_buf == "TRUE" ? true : false);
        } else if (temp_buf.find("dco_support:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_dco_support = (temp_buf == "TRUE" ? true : false);
        } else if (temp_buf.find("drive_capacity:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_drive_capacity = temp_buf.data();
        } else if (temp_buf.find("hpa_capacity:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_hpa_capacity = temp_buf.data();
        } else if (temp_buf.find("dco_capacity:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_dco_capacity = temp_buf.data();
        } else if (temp_buf.find("dco_challenge_key:") != -1) {
            found_pos = temp_buf.find(": ");
            temp_buf.erase(0, found_pos + 2);
            bridge_dco_challenge_key = temp_buf;
        }

        temp_buf.clear();
    }
    pclose(tableau_parm);
}
